; Asm.ProfileAsm: Low level profiling routines
;
; Version 1.00, 04-06-1990
;
; Copyright (C) Ferdinand Oeinck 1990
;
; Modified, Paul Moore 02/06/91.
;   To conform to the conventions of my "Utils" library.
;

a1 RN 0
a2 RN 1
a3 RN 2
a4 RN 3
v1 RN 4
v2 RN 5
v3 RN 6
v4 RN 7
v5 RN 8
v6 RN 9
sl RN 10
fp RN 11
ip RN 12
sp RN 13
lr RN 14
pc RN 15

f0 FN 0
f1 FN 1
f2 FN 2
f3 FN 3
f4 FN 4
f5 FN 5
f6 FN 6
f7 FN 7

r0 RN 0
r1 RN 1
r2 RN 2
r3 RN 3
r4 RN 4
r5 RN 5
r6 RN 6
r7 RN 7
r8 RN 8
r9 RN 9
r10 RN 10
r11 RN 11
r12 RN 12
r13 RN 13
r14 RN 14
r15 RN 15

R0 RN 0
R1 RN 1
R2 RN 2
R3 RN 3
R4 RN 4
R5 RN 5
R6 RN 6
R7 RN 7
R8 RN 8
R9 RN 9
R10 RN 10
R11 RN 11
R12 RN 12
R13 RN 13
R14 RN 14
PC  RN 15

; SWI numbers

XOS_Bit			*	&020000
OS_CallEvery		*	&00003C
OS_RemoveTickerEvent	*	&00003D
Wimp_Poll		*	&0400C7
Wimp_PollIdle		*	&0400E1

; Hard coded address/contents from the OS ROM. Blechh!

OS_check_address	*	&38006CC
OS_check_cont		*	&E58CD108

; Macro to declare C-style function headers
	MACRO
	Func	$name
	ROUT

	LCLS	lab
lab	SETS	"|"
lab	SETS	lab :CC: "$name" :CC: lab

01
	DCB	"$name",0
	ALIGN
	DCD	&FF000000 + {PC} - %BT01
$lab
	MEND

	AREA	|C$$code|, CODE, READONLY

;	Exported functions for this package

	EXPORT	|_profile_ClaimIntDeviceVector|
	EXPORT	|_profile_ReleaseIntDeviceVector|
	EXPORT	|_profile_getstart|
	EXPORT	|_profile_getend|
	EXPORT	|_profile_getlangdesblock|
	EXPORT	|_profile_fp_return_address|

;	Replacement Risc_OSLib functions

	EXPORT	wimp_poll
	EXPORT	wimp_pollidle
	EXPORT	wimp_save_fp_state_on_poll
	EXPORT	wimp_corrupt_fp_state_on_poll

;	Imported functions and data

	IMPORT	binsearch
	IMPORT	|C$$code$$Base|
	IMPORT	|C$$code$$Limit|
	IMPORT	|RTSK$$Data$$Base|
	IMPORT	|_kernel_fpavailable|

;	----------------------------------------------------------------------
;	Function to claim the interrupt and interupt handler
;	void	_profile_ClaimIntDeviceVector (int *adr, int *cnt, int count);
;	----------------------------------------------------------------------

	Func	_profile_ClaimIntDeviceVector

	LDR	a1, |_profile_check|
	LDR	a2, |_profile_check| + 4
	LDR	a1, [a1]
	CMP	a1, a2				; check if running under RISCOS 2.00
	MOVNE	a1, #0
	MOVNES	pc, lr				; no

	MOV	a1, #0
	LDR	a3, [a1, #4]			; load undefined instr vector
	STR	a3, default_undef_vector
	BIC	a3, a3, #&FF000000		; clear branch bits
	MOV	a3, a3, LSL #2
	ADD	a3, a3, #12			; address 
	STR	a3, default_undef_instr		; save undefined instr. address

	ADR	a2, undefined_instr
	SUB	a2, a2, #12
	MOV	a2, a2, LSR #2
	ORR	a2, a2, #&EA000000
	STR	a2, [a1, #4]			; save my undefined instr. address
	STR	a2, my_undef_branch

	MOV	a1, #1				; yes
	ADR	a2, |_profile_every|
	MOV	a3, #0
	SWI	OS_CallEvery

	MOV	a1, #1
	LDR	a2, |_profile_claimed|
	STR	a1, [a2]

	MOVS	pc, lr

|_profile_check|
	&	OS_check_address
	&	OS_check_cont

default_undef_vector
	&	0

my_undef_branch
	&	0

|_profile_claimed|
	&	|x$dataseg| + 4

|_profile_fp_return_address|
	DCD	0

;	----------------------------------------------------------------------
;	Function to release the interrupt and interupt handler
;	void	_profile_ReleaseIntDeviceVector (void);
;	----------------------------------------------------------------------

	Func	_profile_ReleaseIntDeviceVector

	ADR	a1, |_profile_every|
	MOV	a2, #0
	SWI	OS_RemoveTickerEvent

	LDR	a1, default_undef_vector
	MOV	a2, #0
	STR	a1, [a2, #4]			; restore undefined inst vector

	MOV	a1, #0
	LDR	a2, |_profile_claimed|
	STR	a1, [a2]

	MOVS	pc, lr

;	----------------------------------------------------------------------
;	Called every 1/100 second
;	void	_profile_every (void);
;	----------------------------------------------------------------------

	Func	_profile_every

	STMFD	sp!, {a1-a4, ip, lr}
	MOV	a1, #0
	LDR	a2, [a1, #264]			; OS stack storage
	LDR	a1, [a2, #28]			; 8 regs on stack => 7 * 4 = 28
	BIC	a1, a1, #&FC000003
	BL	binsearch
	LDMFD	sp!, {a1-a4, ip, pc}

undefined_instr    
	STMFD	sp!, {a1}
	BIC	a1, lr, #&FC000003		 ; return address
	STR	a1, |_profile_fp_return_address|
	LDMFD	sp!, {a1}
	LDR	pc, default_undef_instr

default_undef_instr
	&	0

;	----------------------------------------------------------------------
;	Get the start of the linker code area
;	void _profile_getstart(void);
;	----------------------------------------------------------------------

	Func	_profile_getstart
	ADR	a1, |_profile_data|
	LDR	a1, [a1, #0]
	MOV	pc, lr

;	----------------------------------------------------------------------
;	Get the end of the linker code area
;	void _profile_getend(void);
;	----------------------------------------------------------------------

	Func	_profile_getend
	ADR	a1, |_profile_data|
	LDR	a1, [a1, #4]
	MOV	pc, lr

;	----------------------------------------------------------------------
;	Get the language description block
;	void _profile_getlangdesblock (void);
;	----------------------------------------------------------------------

|_profile_getlangdesblock|
	ADR	a1, |_profile_data|
	LDR	a1, [a1, #8]
	MOV	pc, lr

|_profile_data|
	DCD	|C$$code$$Base|
	DCD	|C$$code$$Limit|
	DCD	|RTSK$$Data$$Base|


;	----------------------------------------------------------------------
;	Redefinitions of wimp_poll() and wimp_pollidle().
;	To be linked, the exepro library must be linked before Risc_OSLib.
;	----------------------------------------------------------------------

;	----------------------------------------------------------------------
;	Replacement for wimp_poll in Risc_OSLib.
;	os_error *wimp_poll (wimp_emask, wimp_eventstr *);
;	----------------------------------------------------------------------

wimp_poll
	MOV	R12,R13
	STMDB	R13!,{R4,R5,R11,R12,R14,PC}
	SUB	R11,R12,#4
	MOV	R5,R0
	MOV	R4,R1
	LDR	R0, fp_state_ptr
	LDR	R0,[R0],#0
	CMP	R0,#0
	BLNE	|_kernel_fpavailable|
	CMPNE	R0,#0
	BLNE	save_fpe_state
	MOV	R14,R0

	LDR	r0, |_profile_claimed|
	LDR	r0, [r0]
	CMP	r0, #1
	BNE	poll_no_profile1

	ADR	r0, |_profile_every|
	MOV	r1, #0
	SWI	OS_RemoveTickerEvent
	LDR	r0, default_undef_vector
	MOV	r1, #0
	STR	r0, [r1, #4]

poll_no_profile1	      
	MOV	R0,R5
	ADD	R1,R4,#4
	SWI	XOS_Bit:OR:Wimp_Poll
	SUB	R1,R1,#4
	STR	R0,[R1,#0]
	MOVVC	R0,#0
	CMP	R14,#0
	BLNE	restore_fpe_state

	MOV	r5, r0
	LDR	r0, |_profile_claimed|
	LDR	r0, [r0]
	CMP	r0, #1
	BNE	poll_no_profile2

	LDR	a2, my_undef_branch
	MOV	a1, #0
	STR	a2, [a1, #4]
	MOV	a1, #1
	ADR	a2, |_profile_every|
	MOV	a3, #0
	SWI	OS_CallEvery
        
poll_no_profile2
	MOV	r0, r5
	LDMDB	R11,{R4,R5,R11,R13,PC}^

;	----------------------------------------------------------------------
;	Replacement for wimp_pollidle in Risc_OSLib.
;	os_error *wimp_pollidle (wimp_emask, wimp_eventstr *, int);
;	----------------------------------------------------------------------

wimp_pollidle
	MOV	R12,R13
	STMDB	R13!,{R4-R6,R11,R12,R14,PC}
	SUB	R11,R12,#4
	MOV	R5,R0
	MOV	R4,R1
	MOV	R6,R2
	LDR	R0, fp_state_ptr
	LDR	R0,[R0],#0
	CMP	R0,#0
	BLNE	|_kernel_fpavailable|
	CMPNE	R0,#0
	BLNE	save_fpe_state
	MOV	R14,R0

	LDR	r0, |_profile_claimed|
	LDR	r0, [r0]
	CMP	r0, #1
	BNE	pollidle_no_profile1

	ADR	r0, |_profile_every|
	MOV	r1, #0
	SWI	OS_RemoveTickerEvent
	LDR	r0, default_undef_vector
	MOV	r1, #0
	STR	r0, [r1, #4]

pollidle_no_profile1
	MOV	R0,R5
	ADD	R1,R4,#4
	MOV	R2,R6
	SWI	XOS_Bit:OR:Wimp_PollIdle
	CMP	R14,#0
	BLNE	restore_fpe_state
	SUB	R1,R1,#4
	STR	R0,[R1,#0]
	MOVVC	R0,#0

	MOV	r5, r0
	LDR	r0, |_profile_claimed|
	LDR	r0, [r0]
	CMP	r0, #1
	BNE	pollidle_no_profile2

	LDR	a2, my_undef_branch
	MOV	a1, #0
	STR	a2, [a1, #4]
	MOV	a1, #1
	ADR	a2, |_profile_every|
	MOV	a3, #0
	SWI	OS_CallEvery

pollidle_no_profile2
	MOV	r0, r5

	LDMDB	R11,{R4-R6,R11,R13,PC}^

save_fpe_state
	RFS	R1
	STMDB	R13!,{R1}
	MOV	R1,#0
	WFS	R1
	SUB	R13,R13,#&30
	STFE	f4,[R13,#0]
	STFE	f5,[R13,#12]
	STFE	f6,[R13,#24]
	STFE	f7,[R13,#36]
	MOVS	PC,R14

restore_fpe_state
	MOV	R4,#0
	WFS	R4
	LDFE	f4,[R13,#0]
	LDFE	f5,[R13,#12]
	LDFE	f6,[R13,#24]
	LDFE	f7,[R13,#36]
	ADD	R13,R13,#&30
	LDMIA	R13!,{R4}
	WFS	R4
	MOVS	PC,R14

;	----------------------------------------------------------------------
;	Replacement for wimp_save_fp_state_on_poll in Risc_OSLib.
;	void wimp_save_fp_state_on_poll (void);
;	----------------------------------------------------------------------

wimp_save_fp_state_on_poll
	LDR	R0, fp_state_ptr
	MOV	R1,#1
	STR	R1,[R0],#0
	MOVS	PC,R14

;	----------------------------------------------------------------------
;	Replacement for wimp_corrupt_fp_state_on_poll in Risc_OSLib.
;	void wimp_corrupt_fp_state_on_poll (void);
;	----------------------------------------------------------------------

wimp_corrupt_fp_state_on_poll
	LDR	R0, fp_state_ptr
	MOV	R1,#0
	STR	R1,[R0],#0
	MOVS	PC,R14

fp_state_ptr
	&	|x$dataseg|

;	----------------------------------------------------------------------
;	Run time data segment. Note that this module actually stores some of
;	its internal variables in the code segment. This is bad practice, as
;	the code segment is nominally read-only. However, the issue is not so
;	urgent as to require solving (with the consequent code changes) in
;	the immediate future...
;	----------------------------------------------------------------------

	AREA	|C$$data|

|x$dataseg|
	&	0
	&	0

	END
